/**
 * Actualisation des valeurs boursières du SBF250 (Cac All Tradable) en faux temps réel
 * lorsque la bourse de Paris est ouverte.
 * 
 * Le thread est mis en pause lors des fermetures (trop tôt ou tard, WE et jours fériés)
 */

package com.miage.itrading.metier;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.sql.SQLException;
import java.util.Calendar;
import java.util.Date;

import com.miage.itrading.dao.SGBDDao;
import com.miage.itrading.utils.Const;
import com.miage.itrading.utils.Util;

public class TempsReelDaemon extends Thread {

	@SuppressWarnings("deprecation")
	public void run() {

		URL url = null;
		URLConnection urlConn = null;
		InputStreamReader inStream = null;

		// aucune valeur commence par Y
		String[] alphabet = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", 
							 "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};

		final String DEBUT_COTATION		= "<td align=\"center\"><a rel=\"rowlink\" href=\"/cours.phtml?symbole=1rP";
		final String DEBUT_TABLEAU		= "<hr><table width=\"100%\" class=\"btable txt01 T01\"";
		final int DEBUT_COTATION_LENGTH = DEBUT_COTATION.length();

		final String BOURSE_FERMEE		= "(c)";
		final String COURS_VERROUILLE	= "(s)";
		final String COURS_VERROUILLE2	= "(u)";

		String adresse = "http://www.boursorama.com/tableaux/cours_az.phtml?MARCHE=1rPPX5&LETTRE=";
		String line;
		String[] lineSplitted;

		String mnemonique, nom;
		float valeurCourante;
		long volume;

		SGBDDao s = new SGBDDao();

		s.initBufferValeursCourantes();

		Calendar aujourdhui = Calendar.getInstance();
		int seconde;

		Date ouvertureBourse = (Date) (Const.HEURE_OUVERTURE.clone());
		Date fermetureBourse = (Date) (Const.HEURE_FERMETURE.clone());

		// début du daemon
		while (true) {

			/**
			 * Ne récupère les valeurs que du lundi au vendredi
			 * Endort le thread jusqu'au prochain lundi 09:00 si on est le WE
			 */
			if ((aujourdhui.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) || (aujourdhui.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY)) {
				try {
					System.out.println("Nous sommes le : "+ Calendar.DAY_OF_WEEK);
					System.out.println("C'est le WE dude, le daemon est en pause jusqu'à lundi, see ya!  A dans "
									+ (sleepUntilNextOpening(1) / 60000)
									+ "min alors!");

					Thread.sleep(sleepUntilNextOpening(7 - aujourdhui.get(Calendar.DAY_OF_WEEK)));

				} catch (InterruptedException e) {
					System.out.println("T'as arrêté le serveur pendant qu'il dormait, salaud! xD");
				}
			}
			// La bourse est ouverte aujourd'hui, yihaaaaaaa
			else {
				ouvertureBourse.setDate(aujourdhui.get(Calendar.DAY_OF_MONTH));
				ouvertureBourse.setMonth(aujourdhui.get(Calendar.MONTH));
				ouvertureBourse.setYear(aujourdhui.get(Calendar.YEAR) - 1900);

				fermetureBourse.setDate(aujourdhui.get(Calendar.DAY_OF_MONTH));
				fermetureBourse.setMonth(aujourdhui.get(Calendar.MONTH));
				fermetureBourse.setYear(aujourdhui.get(Calendar.YEAR) - 1900);
			}

			// il est trop tôt
			if (aujourdhui.getTime().before(ouvertureBourse)) {
				try {
					System.out.println("You're an early bird, aren't you? Patience you must learn, young one. A dans "
									+ (sleepUntilNextOpening(1) / 60000)
									+ "min alors!");

					Thread.sleep(sleepUntilNextOpening(0));

				} catch (InterruptedException e) {
					System.out.println("T'as arrêté le serveur pendant qu'il dormait, salaud! xD");
				}
			}
			// il est trop tard! on doit attendre demain maintenant
			// on refait une itération car si on est vendredi, demain c'est fermé...
			else if (aujourdhui.getTime().after(fermetureBourse)) {
				try {
					System.out.println("Oops you're too late, tieeeeeeeeeeeeeeens!  A dans "
									+ (sleepUntilNextOpening(1) / 60000)
									+ "min alors!");

					Thread.sleep(sleepUntilNextOpening(1));
					continue;

				} catch (InterruptedException e) {
					System.out.println("T'as arrêté le serveur pendant qu'il dormait, salaud! xD");
				}
			} else {

				/**
				 * Endort le serveur jusqu'au prochain data retrieval
				 */
				seconde = aujourdhui.get(Calendar.SECOND);

				if ((seconde % Const.INTERVALLE_TEMPS_REEL) != 0) {
					try {
						Thread.sleep((seconde - (Const.INTERVALLE_TEMPS_REEL % Const.INTERVALLE_TEMPS_REEL)) * 1000);

					} catch (InterruptedException e) {
						System.out.println("T'as arrêté le serveur pendant qu'il dormait, salaud! xD");
					}
				}

				// Ne récupère que les valeurs dans le tableau principal, utilise le garde-fou
				// "</table>" comme fin de tableau
				boolean dansTableau;

				aujourdhui = Calendar.getInstance();

				/**
				 * Récupère toutes les valeurs du SBF250
				 */
				
				for (int i=0; i<26; ++i) {
					try {
						url 				= new URL(adresse + alphabet[i]);
						urlConn				= url.openConnection();
						inStream			= new InputStreamReader(urlConn.getInputStream());
						BufferedReader buff = new BufferedReader(inStream);

						dansTableau			= false;

						while ((line = buff.readLine()) != null) {

							// Mnémonique et nom
							//
							// <td align="center"><a rel="rowlink"
							// href="/cours.phtml?symbole=1rPABCA"
							// title="ABC ARBITRAGE"><img
							// src="http://img.boursorama.com/i/block/stable.gif"
							// width="11px" height="9px" border="0" alt="" /></a></td>
							if (line.startsWith(DEBUT_TABLEAU)) {
								dansTableau = true;
							}
							else if (dansTableau && line.startsWith(DEBUT_COTATION)) {
								line = line.substring(DEBUT_COTATION_LENGTH);
								lineSplitted = line.split("\"");

								if (lineSplitted.length >= 3) {

									mnemonique = lineSplitted[0];
									nom = lineSplitted[2];

									// passe les deux lignes suivantes
									for (int j = 0; (j < 3) && (line != null); ++j) {
										line = buff.readLine();
									}

									// Valeur courante
									//
									// <td align="right">30.825(c)</td>
									if (line != null) {
										// length(<td align="right">) = 18
										// length(</td>) = 5
										line = line.trim();
										line = line.substring(18, line.length() - 5);

										if (line.endsWith(BOURSE_FERMEE) == false) {
											if ((line.endsWith(COURS_VERROUILLE) == false)
												 && (line.endsWith(COURS_VERROUILLE2) == false)) {

												valeurCourante = Float.parseFloat(line);

												// passe les cinq lignes suivantes (variation, +
												// haut, +
												// bas,
												// ouverture, variation 1er janvier)
												for (int j = 0; (j < 6) && (line != null); ++j) {
													line = buff.readLine();
												}

												// Volume
												//
												// <td align="right">880 698</td>
												if (line != null) {
													// length(<td align="right">) = 18
													// length(</td>) = 5
													line = line.trim();
													line = line.substring(18, line.length() - 5);

													// retire les séparateurs tous les 3 chiffres
													line = line.replaceAll(" ", "");

													volume = Long.parseLong(line);

													s.addBufferValeurCourante(
															mnemonique, nom,
															valeurCourante,
															volume);
												}
											}
										}
										// cette cotation n'est pas ouverte ou la bourse est fermée
//										else { }
									}
								}
							}
							/**
							 * Si on est dans le tableau et que l'on lit la fin de tableau, on passe
							 * à la page suivante
							 */
							else if (dansTableau == true) {
								line = line.trim();

								if (line.equalsIgnoreCase("</table>") == true) {
									break;
								}
							}
						}
					} catch (MalformedURLException e1) {
						e1.printStackTrace();
					} catch (IOException e2) {
						e2.printStackTrace();
					}
				}

				// vide le buffer avec toutes les valeurs relevées
				try {
					s.flushValeursCourantes();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * Durée en millisecondes jusqu'à la prochaine ouverture de la bourse (dans "jours" jours)
	 * 
	 * @param jours
	 * @return
	 */
	@SuppressWarnings("deprecation")
	private Long sleepUntilNextOpening(int jours) {
		Calendar aujourdhui = Calendar.getInstance();

		Calendar prochaineOuverture = Calendar.getInstance();
		prochaineOuverture.add(Calendar.DAY_OF_MONTH, jours);

		prochaineOuverture.set(prochaineOuverture.get(Calendar.YEAR),
							   prochaineOuverture.get(Calendar.MONTH),
							   prochaineOuverture.get(Calendar.DAY_OF_MONTH),
							   Const.HEURE_OUVERTURE.getHours(), Const.HEURE_OUVERTURE.getMinutes(), 0);
		
		Util.tempsReelPause = (prochaineOuverture.getTimeInMillis() - aujourdhui.getTimeInMillis());

		return Util.tempsReelPause;
	}

}
